﻿using System;
using System.Collections.Generic;
using System.Linq;
using Exiled.API.Enums;
using Exiled.API.Features;

namespace RgCore.CustomSide
{
    public struct RoundInfo
    {
        /// <summary>
        /// 阵营的存活数
        /// </summary>
        public IReadOnlyDictionary<string, byte> SideAlives;
        public byte ClassD_Alives;
        public byte Scientist_Alives;
        public byte Chaos_Alives;
        public byte Scp_Alives;
        public byte Mtf_Alives;
        public byte AlivesWithoutNeutral;
        public byte TotalAlives;
        public byte Zombies;
        public byte ChaosTargetCount;
    }
    public class CustomSide
    {
        public CustomSide(string id, string sideName, string sideColor, bool isFf, Func<RoundInfo, bool?> winningcondition = null)
        {
            Id = id;
            SideName = sideName;
            SideColor = sideColor;
            FriendlyFire = isFf;
            WinningCondition = winningcondition;
        }

        public string Id { get; }
        public bool FriendlyFire { get; set; }
        public string FormattedName => $"<color={SideColor}>{SideName}</color>";
        public string SideName { get; set; }
        public string SideColor { get; set; }
        /// <summary>
        /// 获胜条件，如果这个值为null，则为不影响对局结束
        /// <para>True: 该阵营满足回合结束条件，并满足回合胜利条件</para>
        /// <para>False: 该阵营满足回合结束条件，但不满足回合胜利条件（平局）</para>
        /// <para>Null: 该阵营不满足回合结束条件</para>
        /// </summary>
        public Func<RoundInfo, bool?> WinningCondition { get; set; }
        public List<string> FriendlySideIds { get; set; } = new List<string>();

        public override bool Equals(object obj)
        {
            return obj is CustomSide side && Id == side.Id;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        public static CustomSide TryCreate(string id, string sideName, string sideColor, bool ff = false, Func<RoundInfo, bool?> winningcondition = null)
        {
            var side = SideManager.GetSideById(id);
            if (side == null)
            {
                side = new CustomSide(id, sideName, sideColor, ff, winningcondition);
                SideManager.Sides.Add(side);
            }
            return side;
        }
        public static CustomSide TryCreate(string id)
        {
            return TryCreate(id, id, "#FFFFFF");
        }

        public static bool operator ==(CustomSide A, Side B)
        {
            return A?.Id == B.ToString();
        }
        public static bool operator !=(CustomSide A, Side B)
        {
            return !(A == B);
        }

        public static implicit operator CustomSide(Side source)
        {
            return SideManager.Sides.FirstOrDefault(x => x.Id == source.ToString());
        }
    }
    public static class SideManager
    {
        public static List<CustomSide> Sides = new List<CustomSide>()
        {
            new CustomSide(Side.Scp.ToString(), "SCP", global::Misc.AllowedColors[global::Misc.PlayerInfoColorTypes.Red], false, (info) =>
            {
                int human = info.SideAlives["Human"];
                int chaos = info.Chaos_Alives;
                int scp = info.Scp_Alives;
                int zombie = info.Zombies;
                int total = info.AlivesWithoutNeutral;
                if (chaos + scp + zombie != total)
                    return null;
                if (info.ChaosTargetCount != 0)
                    return null;
                return scp >= RoundSummary.EscapedClassD + RoundSummary.EscapedScientists || RoundSummary.EscapedClassD == RoundSummary.EscapedScientists;
            }),
            new CustomSide(Side.ChaosInsurgency.ToString(), "混沌分裂者", global::Misc.AllowedColors[global::Misc.PlayerInfoColorTypes.Green], false, (info) =>
            {
                int human = info.SideAlives["Human"];
                int chaos = info.Chaos_Alives;
                int scp = info.Scp_Alives;
                int total = info.AlivesWithoutNeutral;
                int zombie = info.Zombies;
                int classd = info.ClassD_Alives;
                if (chaos + scp + human + zombie == total && info.ChaosTargetCount == 0)
                {
                    return RoundSummary.EscapedClassD > RoundSummary.EscapedScientists + scp;
                }
                else if (chaos + classd + human == total)
                {
                    return RoundSummary.EscapedClassD >= RoundSummary.EscapedScientists;
                }
                return null;
            })
            {
                FriendlySideIds = { "Human"}
            },
            new CustomSide(Side.Mtf.ToString(), "机动特遣队", global::Misc.AllowedColors[global::Misc.PlayerInfoColorTypes.Cyan], false, (info) =>
            {
                int human = info.SideAlives["Human"];
                int scientist = info.Scientist_Alives;
                int mtf = info.Mtf_Alives;
                int scp = info.Scp_Alives;
                int total = info.AlivesWithoutNeutral;
                if (scientist + mtf + human == total)
                {
                    return RoundSummary.EscapedScientists >= RoundSummary.EscapedClassD;
                }
                return null;
            })
            {
                FriendlySideIds = { "Human"}
            },
            new CustomSide(Side.None.ToString(), "中立", global::Misc.AllowedColors[global::Misc.PlayerInfoColorTypes.Magenta], true),
            new CustomSide("Human", "人类", global::Misc.AllowedColors[global::Misc.PlayerInfoColorTypes.Yellow], false)
            {
                FriendlySideIds = {Side.Mtf.ToString(), Side.ChaosInsurgency.ToString()}
            },
        };
        public static Dictionary<Player, CustomSide> CustomedSide = new Dictionary<Player, CustomSide>();
        public static bool IsFriendly(this Player player, Player target)
        {
            var ply = GetSide(player);
            var tar = GetSide(target);
            return IsFriendly(ply, tar) || !player.IsAlive || !target.IsAlive;
        }
        public static bool IsFriendly(this CustomSide player, CustomSide target)
        {
            return player == target || player.FriendlySideIds.Contains(target.Id);
        }
        public static CustomSide GetSide(this Side side) => GetSideById(side.ToString());
        public static CustomSide GetSide(this Player player)
        {
            if (CustomedSide.TryGetValue(player, out var val))
            {
                return val;
            }
            if (player.Role.Type == PlayerRoles.RoleTypeId.Tutorial)
                return Side.None;
            return player.Role.Side.GetSide();
        }
        public static CustomSide GetSideById(this string id) =>
            Sides.FirstOrDefault(x => x.Id == id);
    }
}
